package com.ctsi.framework.yaml.support;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 配置信息模块化引入Environment
 * 主要目的是设置spring.autoconfig.exclud等需要在bean初始化之前设置的配置项
 * 还有就是设置系统默认参数
 * @author zhangjw
 */
public class ModuleEnvironmentPostProcessor implements EnvironmentPostProcessor,Ordered {

    private static final Logger logger = LoggerFactory.getLogger(ModuleEnvironmentPostProcessor.class);

    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 11;

    private static final String RESOURCE_PATH = "classpath*:/META-INF/ctsi-cloud-default";

    private int order = DEFAULT_ORDER;

    /**
     * Post-process the given {@code environment}.
     *
     * @param environment the environment to post-process
     * @param application the application to which the environment belongs
     */
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        logger.info("加载模块Environment信息开始!");
        new Loader(environment,application.getResourceLoader()).load();
//        ResourceLoader resourceLoader = application.getResourceLoader();
//        MutablePropertySources destination = environment.getPropertySources();
//        YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
//        String name = "applicationConfig: [" + RESOURCE_PATH + "]";
//
//        if(resourceLoader != null && resourceLoader instanceof  ResourcePatternResolver){
//            load((ResourcePatternResolver)resourceLoader,loader,name,destination);
//        }else{
//            load(new PathMatchingResourcePatternResolver(),loader,name,destination);
//        }
        logger.info("加载模块Environment信息结束!");
    }

//    private void load(ResourcePatternResolver resourcePatternResolver,YamlPropertySourceLoader loader,String name,MutablePropertySources destination){
//        try {
//            Arrays.stream(resourcePatternResolver.getResources(RESOURCE_PATH)).forEach(resource -> {
//                try {
//                    List<PropertySource<?>> pss = loader.load(name,resource);
//                    pss.stream().forEach(propertySource -> {
//                        destination.addLast(propertySource);
//                    });
//                } catch (IOException e) {
//                    logger.error("{}",e);
//                }
//            });
//        } catch (IOException e) {
//            logger.error("{}",e);
//        }
//    }

    private static class Loader {

        private final ConfigurableEnvironment environment;

        private final ResourcePatternResolver resourceLoader;

        private final List<PropertySourceLoader> propertySourceLoaders;

        Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            this.environment = environment;
            if(resourceLoader != null && resourceLoader instanceof ResourcePatternResolver){
                this.resourceLoader = (ResourcePatternResolver) resourceLoader;
            } else {
                this.resourceLoader = new PathMatchingResourcePatternResolver();
            }
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
        }

        void load() {
            for (PropertySourceLoader loader : propertySourceLoaders) {
                for (String extension : loader.getFileExtensions()) {
                    String location = RESOURCE_PATH + "." + extension;
                    load(location, loader);
                }
            }

        }

        void load(String location, PropertySourceLoader loader) {
            try {

                Resource[] resources = resourceLoader.getResources(location);

                Arrays.stream(resources).filter(resource -> resource.exists()).forEach(resource -> {
                    List<PropertySource<?>> propertySources = null;
                    try {
                        String propertyResourceName = "ctsiCloudDefaultConfig: [" + resource.getURL() + "]";
                        propertySources = loader.load(propertyResourceName, resource);
                    } catch (IOException e) {
                        throw new IllegalStateException("Failed to load property source from location '" + location + "'", e);
                    }
                    Optional.ofNullable(propertySources)
                            .ifPresent(sources -> sources.forEach(source -> environment.getPropertySources().addLast(source)));
//                    if (propertySources == null) {
//                        return;
//                    }
//                    propertySources.forEach(source -> environment.getPropertySources().addLast(source));
                });
            } catch (Exception ex) {
                throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
            }
        }
    }

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     *
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    @Override
    public int getOrder() {
        return this.order;
    }
}
